package com.mxpio.erp.hr.service.impl;

import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import com.mxpio.erp.common.enums.HrEnums;
import com.mxpio.erp.common.enums.OrderStatusEnums;
import com.mxpio.erp.common.hr.entity.AttendanceCalendar;
import com.mxpio.erp.common.hr.entity.AttendanceRecorder;
import com.mxpio.erp.common.hr.service.AttendanceRecorderService;
import com.mxpio.erp.common.oa.entity.OaOvertimeApply;
import com.mxpioframework.common.vo.Result;
import com.mxpioframework.jpa.JpaUtil;
import com.mxpioframework.jpa.lin.Linq;
import com.mxpioframework.security.entity.Post;
import com.mxpioframework.security.entity.User;
import com.mxpioframework.security.service.DeptService;
import com.mxpioframework.system.service.impl.BaseServiceImpl;
import com.mxpioframework.wechat.cp.config.single.WxCpConfiguration;
import lombok.extern.slf4j.Slf4j;
import me.chanjar.weixin.common.error.WxErrorException;
import me.chanjar.weixin.cp.api.WxCpOaService;
import me.chanjar.weixin.cp.api.WxCpService;
import me.chanjar.weixin.cp.api.impl.WxCpOaServiceImpl;
import me.chanjar.weixin.cp.bean.oa.WxCpCheckinDayData;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.time.DateUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.stream.Collectors;


@Service
@Slf4j
public class AttendanceRecorderServiceImpl extends BaseServiceImpl<AttendanceRecorder> implements AttendanceRecorderService {
    @Value("${wechat.cp.ignoreUsers}")
    private String ignoreUserids;
    @Value("${wechat.cp.appConfigs[0].agentId}")
    private Integer agentId;

    @Resource
    private DeptService deptService;
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Result<?> syncCheckinDataFromCp(Integer year, Integer month) {
        Linq linq = JpaUtil.linq(User.class);
        if (StringUtils.isNotBlank(ignoreUserids)) {
            linq.notIn("username", Arrays.asList(ignoreUserids.split(",")));
        }
        List<User> users = linq.list();
        if (users!=null && users.size()>0){
            List<String> userNameList = users.stream().filter(obj -> obj.getThirdId() != null && !obj.getThirdId().isEmpty()).map(per -> per.getThirdId()).collect(Collectors.toList());
            if (userNameList.isEmpty()){
                return Result.OK("根据thirdId过滤，没找到需要同步的用户");
            }

            WxCpService wxCpService =  WxCpConfiguration.getCpService(agentId);
            WxCpOaService wxCpOaService = new WxCpOaServiceImpl(wxCpService);
            Calendar startTime = Calendar.getInstance();
            Calendar endTime = Calendar.getInstance();
            startTime.set(year, month-1, 1);
            endTime.set(year, month-1, startTime.getActualMaximum(Calendar.DAY_OF_MONTH));
            List<AttendanceRecorder> attendanceRecorderList = new ArrayList<>();
            int batchSize = 100;
            for (int i = 0; i < userNameList.size(); i += batchSize) {
                int endIndex = Math.min(i + batchSize, userNameList.size());
                List<String> subBatch = userNameList.subList(i, endIndex);
                // 这里可以添加每一百条数据的处理逻辑
                try {
                    //List<WxCpCheckinData> checkinData = wxCpOaService.getCheckinData(3, startTime.getTime(), endTime.getTime(), subBatch);
                    List<WxCpCheckinDayData> checkinDayData = wxCpOaService.getCheckinDayData(startTime.getTime(), endTime.getTime(), subBatch);
                    if (checkinDayData!=null && checkinDayData.size()>0){
                        Map<String,User> userMap = new HashMap<>();
                        Map<String,String> deptCodeMap = new HashMap<>();
                        for (WxCpCheckinDayData checkinDay : checkinDayData) {
                            WxCpCheckinDayData.BaseInfo baseInfo = checkinDay.getBaseInfo();
                            String acctId = baseInfo.getAcctId();
                            User dbUser = null;
                            if (userMap.containsKey(acctId)) {
                                dbUser = userMap.get(acctId);
                            }else{
                                dbUser = JpaUtil.linq(User.class).equal("thirdId", acctId).findOne();
                                userMap.put(acctId, dbUser);
                            }
                            if (dbUser==null){
                                log.error("thirdId:{}的用户不存在",acctId);
                                continue;
                            }

                            String deptCode = null;
                            if (deptCodeMap.containsKey(dbUser.getUsername())){
                                deptCode =   deptCodeMap.get(dbUser.getUsername());
                            }else{
                                Set<String> deptCodes = deptService.getDeptKeysByUser(dbUser.getUsername(), "code");
                                if(!deptCodes.isEmpty()){
                                    deptCode = deptCodes.toArray()[0].toString();
                                    deptCodeMap.put(dbUser.getUsername(), deptCode);
                                }
                            }

                            WxCpCheckinDayData.SummaryInfo summaryInfo = checkinDay.getSummaryInfo();
                            if (summaryInfo.getCheckinCount()==0){
                                continue;//节假日不打卡或请假时打卡次数为0
                            }
                            Long dateTime = Long.valueOf(baseInfo.getDate());
                            Long earliestTime = dateTime+Long.valueOf(summaryInfo.getEarliestTime());
                            Long lastestTime = dateTime+Long.valueOf(summaryInfo.getLastestTime());//缺卡时，lastestTime=earliestTime，可能是上班缺卡，也可能是下班缺卡
                            Long checkinTime = dateTime*1000;//秒转毫秒
                            earliestTime = earliestTime*1000;//秒转毫秒
                            lastestTime = lastestTime*1000;//秒转毫秒
                            Calendar calendar = DateUtil.calendar(checkinTime);
                            Calendar checkInCal = DateUtil.calendar(earliestTime);
                            Calendar checkOutCal = DateUtil.calendar(lastestTime);
                            String date = DateUtil.toLocalDateTime(calendar).format(DateTimeFormatter.ofPattern("yyyy-MM-dd"));



                            AttendanceRecorder attendanceRecorder = new AttendanceRecorder();
                            attendanceRecorder.setUsername(dbUser.getUsername());
                            attendanceRecorder.setNickname(dbUser.getNickname());
                            attendanceRecorder.setDate(date);
                            attendanceRecorder.setDeptCode(deptCode);
                            attendanceRecorder.setCheckInTime(checkInCal.getTime());
                            attendanceRecorder.setCheckOffTime(checkOutCal.getTime());

                            if (earliestTime.longValue()!=lastestTime.longValue()){//没有缺卡的情况时
                                LocalTime checkInTime = DateUtil.toLocalDateTime(checkInCal).toLocalTime();
                                LocalTime dayShiftStart = LocalTime.of(12, 0);
                                if(checkInTime.isAfter(dayShiftStart)){//ShiftType后续会用来判断是否缺卡
                                    attendanceRecorder.setShiftType(HrEnums.AttendanceShiftType.NIGHT.getCode());
                                }else{
                                    attendanceRecorder.setShiftType(HrEnums.AttendanceShiftType.DAY.getCode());
                                }
                            }
                            attendanceRecorderList.add(attendanceRecorder);
                        }
                    }
                } catch (WxErrorException e) {
                    return Result.error("从企业微信获取用户打卡日报记录失败："+e.getMessage());
                }
            }
            if (!attendanceRecorderList.isEmpty()){
                //处理历史数据
                String startDate = DateUtil.formatDate(startTime.getTime());
                String endDate = DateUtil.formatDate(endTime.getTime());
                JpaUtil.lind(AttendanceRecorder.class).between("date",startDate,endDate).delete();
                JpaUtil.save(attendanceRecorderList);//持久化
            }
            return Result.OK("同步成功",null);
        }
        return Result.error("未匹配到用户数据");
    }

    @Override
    @Transactional
    public Result<?> calculateWorkingHours(Integer year, Integer month) {
        if (year==null || month==null){
            return Result.error("year或month不能为空");
        }
        Calendar startTime = Calendar.getInstance();
        Calendar endTime = Calendar.getInstance();
        startTime.set(year, month-1, 1);
        endTime.set(year, month-1, startTime.getActualMaximum(Calendar.DAY_OF_MONTH));
        //处理历史数据
        String startDate = DateUtil.formatDate(startTime.getTime());
        String endDate = DateUtil.formatDate(endTime.getTime());
        List<AttendanceRecorder> recorders = JpaUtil.linq(AttendanceRecorder.class).between("date", startDate, endDate).list();
        if (recorders.isEmpty()){
            return Result.error("未获取到该月份用户考勤打卡记录，请先同步打卡数据！");
        }
        List<AttendanceCalendar> calendarList = JpaUtil.linq(AttendanceCalendar.class).between("date", startDate, endDate).list();
        if (calendarList.isEmpty()){
            return Result.error("未获取到该月份的考勤日历，请先同步日历数据！");
        }
        Map<Object, AttendanceCalendar> calendarMap = JpaUtil.index(calendarList, "date");

        List<Post> postList = JpaUtil.linq(Post.class).list();
        Map<String, Post> postMap = null;
        if (!postList.isEmpty()){
            postMap = JpaUtil.index(postList);
        }

        Map<String, List<AttendanceRecorder>> recorderMap = recorders.stream().collect(Collectors.groupingBy(AttendanceRecorder::getUsername));

        for (String userName:recorderMap.keySet()) {//开始测算
            List<AttendanceRecorder> userRecorders = recorderMap.get(userName);
            User user = JpaUtil.getOne(User.class, userName);
            if (user==null){
                log.error("username:{}的用户不存在",userName);
                userRecorders.forEach(per -> per.setErrorMsg("username:对应的用户不存在"));
                JpaUtil.update(userRecorders);
                continue;
            }
            if (postMap==null || !postMap.containsKey(user.getPostId())){
                log.error("username:{}用户的岗位异常",userName);
                userRecorders.forEach(per -> per.setErrorMsg("岗位信息异常或用户岗位未指定"));
                JpaUtil.update(userRecorders);
                continue;
            }
            Post post = postMap.get(user.getPostId());
            for (AttendanceRecorder recorder:userRecorders){
                String date = recorder.getDate();//日期
                if (!calendarMap.containsKey(date)){
                    recorder.setErrorMsg("日期未匹配到对应的考勤日历！");
                    recorder.setWorkingHours(BigDecimal.ZERO);
                    continue;
                }
                AttendanceCalendar attendanceCalendar = calendarMap.get(date);
                recorder.setDateType(attendanceCalendar.getDateType());//关联日历类型

                if (StringUtils.isBlank(recorder.getShiftType())){
                    recorder.setErrorMsg("缺卡");
                    recorder.setWorkingHours(BigDecimal.ZERO);
                    continue;
                }
                Date checkInTime = recorder.getCheckInTime();
                Date checkOffTime = recorder.getCheckOffTime();
                if(StringUtils.equals("一线操作员",post.getName())){
                    //查询报工记录
                    /*List<ProdDailyPaper> dailyPapers = JpaUtil.linq(ProdDailyPaper.class).equal("employeeId", userName).equal("reportTime", date).list();
                    if (dailyPapers.isEmpty()){
                        recorder.setWorkingHours(BigDecimal.ZERO);
                        recorder.setErrorMsg("该日期未找到对应的报工工时记录！");
                    }else{
                        // 计算 workingHours 的总和
                        BigDecimal totalWorkingHours = dailyPapers.stream().filter(prodDailyPaper -> prodDailyPaper.getOvertimeHours()!=null)
                                .map(ProdDailyPaper::getOvertimeHours)
                                .reduce(BigDecimal.ZERO, BigDecimal::add);
                        recorder.setWorkingHours(totalWorkingHours);
                        String detailMsg = dailyPapers.stream().filter(prodDailyPaper -> prodDailyPaper.getOvertimeHours()!=null)
                                .map(ProdDailyPaper::getOvertimeHours)
                                .map(BigDecimal::toString)
                                .collect(Collectors.joining("+ "));
                        recorder.setDetailMsg("加班工时:"+detailMsg);
                    }*/
                    recorder.setWorkingHours(BigDecimal.ZERO);
                    recorder.setErrorMsg("该日期未找到对应的报工工时记录！");
                }else if(StringUtils.equals("班组长",post.getName())){
                    //计算工时
                    boolean am = DateUtil.isAM(checkInTime);
                    if (am){//白班
                        String fiveThirty = date+" 17:30:00";
                        String sevenTwenty = date+" 19:20:00";
                        String sevenThirty = date+" 19:30:00";
                        if (checkOffTime.after(DateUtil.parse(sevenThirty))){//超过晚上七点半
                            //汇总加班申请表
                            List<OaOvertimeApply> overtimeApplies = JpaUtil.linq(OaOvertimeApply.class)
                                    .equal("employeeId", userName)
                                    .equal("bpmnStatus", OrderStatusEnums.BpmnStatus.AUDITED.getStatusCode())
                                    .equal("applyTime", DateUtil.parse(date)).list();
                            BigDecimal totalWorkingHours = overtimeApplies.stream()
                                    .map(OaOvertimeApply::getWorkingHours)
                                    .reduce(BigDecimal.ZERO, BigDecimal::add);
                            recorder.setWorkingHours(totalWorkingHours);

                            String detailMsg = overtimeApplies.stream()
                                    .map(OaOvertimeApply::getWorkingHours)
                                    .map(BigDecimal::toString)
                                    .collect(Collectors.joining("+ "));
                            recorder.setDetailMsg("OA加班申请工时："+detailMsg);
                        } else if (checkOffTime.after(DateUtil.parse(sevenTwenty))&&checkOffTime.before(DateUtil.parse(sevenThirty))) {//19:20-19:30打卡算2个小时
                            recorder.setWorkingHours(new BigDecimal("2"));
                        }else if (checkOffTime.after(DateUtil.parse(fiveThirty))&&checkOffTime.before(DateUtil.parse(sevenTwenty))) {//17:30-19:20之后打卡算1
                            recorder.setWorkingHours(new BigDecimal("1"));
                        }else{
                            recorder.setWorkingHours(BigDecimal.ZERO);//为零
                        }
                    }else{//夜班
                        DateTime parse = DateUtil.parse(date);
                        Date nextDay = DateUtils.addDays(parse, 1);
                        String nextDayStr = DateUtil.formatDate(nextDay);
                        String fiveThirtyAm = nextDayStr+" 05:30:00";
                        String sixThirtyAm = nextDayStr+" 06:30:00";
                        String sevenThirtyAm = nextDayStr+" 07:30:00";
                        if (checkOffTime.after(DateUtil.parse(sevenThirtyAm))){//超过早上七点半
                            //汇总加班申请表
                            List<OaOvertimeApply> overtimeApplies = JpaUtil.linq(OaOvertimeApply.class)
                                    .equal("employeeId", userName)
                                    .equal("bpmnStatus", OrderStatusEnums.BpmnStatus.AUDITED.getStatusCode())
                                    .equal("applyTime", DateUtil.parse(date)).list();
                            BigDecimal totalWorkingHours = overtimeApplies.stream()
                                    .map(OaOvertimeApply::getWorkingHours)
                                    .reduce(BigDecimal.ZERO, BigDecimal::add);
                            recorder.setWorkingHours(totalWorkingHours);

                            String detailMsg = overtimeApplies.stream()
                                    .map(OaOvertimeApply::getWorkingHours)
                                    .map(BigDecimal::toString)
                                    .collect(Collectors.joining("+ "));
                            recorder.setDetailMsg("OA加班申请工时："+detailMsg);
                        } else if (checkOffTime.after(DateUtil.parse(sixThirtyAm))&&checkOffTime.before(DateUtil.parse(sevenThirtyAm))) {//7:20-7:30打卡算2个小时
                            recorder.setWorkingHours(new BigDecimal("2"));
                        }else if (checkOffTime.after(DateUtil.parse(fiveThirtyAm))&&checkOffTime.before(DateUtil.parse(sixThirtyAm))) {//5:30-6:30之后打卡算1
                            recorder.setWorkingHours(new BigDecimal("1"));
                        }else{
                            recorder.setWorkingHours(BigDecimal.ZERO);//为零
                        }
                    }
                }else{//其他部门
                    String fiveThirtyPm = date+" 17:30:00";
                    String sevenTwentyPm = date+" 19:20:00";
                    String sevenThirtyPm = date+" 19:30:00";

                    if (checkOffTime.after(DateUtil.parse(sevenThirtyPm))){//超过晚上七点半
                        //汇总OA加班申请表
                        List<OaOvertimeApply> overtimeApplies = JpaUtil.linq(OaOvertimeApply.class)
                                .equal("employeeId", userName)
                                .equal("bpmnStatus", OrderStatusEnums.BpmnStatus.AUDITED.getStatusCode())
                                .equal("applyTime", DateUtil.parse(date)).list();
                        BigDecimal totalWorkingHours = overtimeApplies.stream()
                                .map(OaOvertimeApply::getWorkingHours)
                                .reduce(BigDecimal.ZERO, BigDecimal::add);
                        recorder.setWorkingHours(totalWorkingHours);

                        String detailMsg = overtimeApplies.stream()
                                .map(OaOvertimeApply::getWorkingHours)
                                .map(BigDecimal::toString)
                                .collect(Collectors.joining("+ "));
                        recorder.setDetailMsg("OA加班申请工时："+detailMsg);
                    } else if (checkOffTime.after(DateUtil.parse(sevenTwentyPm))&&checkOffTime.before(DateUtil.parse(sevenThirtyPm))) {//19:20-19:30打卡算2个小时
                        recorder.setWorkingHours(new BigDecimal("2"));
                    }else if (checkOffTime.after(DateUtil.parse(fiveThirtyPm))&&checkOffTime.before(DateUtil.parse(sevenTwentyPm))) {//17:30-19:20之后打卡算1
                        recorder.setWorkingHours(new BigDecimal("1"));
                    }else{
                        recorder.setWorkingHours(BigDecimal.ZERO);//为零
                    }
                }
            }
        }
        JpaUtil.update(recorders);
        return Result.OK("加班工时测算成功",null);
    }
}
